#ifndef SCRIPTPROCESSOR_H
#define SCRIPTPROCESSOR_H

#include <QtCore>
#include <QtScript>
#include <QtXml>

#include "DataSource.h"
#include "ELFReader.h"

class ScriptDataIterator;
class ScriptProcessor;

class ScriptContext
{
public:
    ScriptContext();
    
public:
    QString bufferFilePath;
    QString elfFilePath;
    bool swapBytes;
    
    int iteratorIndex;
    int iteratorTotal;
    QScriptValue matcherIteratorValue;
    QScriptValue actionIteratorValue;
    QScriptValue* eventData;
    
    DataSource* dataSource;
    
    ScriptDataIterator* dataIterator;
    ScriptProcessor* processor;
    ELFReader* elfReader;
};


class ScriptDataIterator
{
public:
    ScriptDataIterator(ScriptContext* context);
    
    void setUp(const QDomNode& node);

    bool hasNext();
    void next();
    QScriptValue at(int index, QScriptEngine* engine);
    QScriptValue value(QScriptEngine* engine);
    
private:
    ScriptContext* context;
    QByteArray data;
    int activeEvent;
    int totalEvents;
};


class ScriptDataBinding
{
public:
    ScriptDataBinding();
    ScriptDataBinding(ScriptContext* context);
    ScriptDataBinding(const ScriptDataBinding& value);
    ScriptDataBinding& operator=(const ScriptDataBinding& value);
    
    void setUp(const QDomNode& node);
    void print() const;
    
    void preProcess(QScriptEngine* matchEngine, QScriptEngine* actionEngine);
    void postProcess(QScriptEngine* matchEngine, QScriptEngine* actionEngine);
    void createBindingPropertiesForHardCompile(QScriptEngine* actionEngine);

public:
    QString name;

private:
    enum BindingType { iterator, stateless, statefull };
    
private:
    ScriptContext* context;
    BindingType type;
    QStringList keys;
};


class ScriptRule
{
public:
    ScriptRule(QString matcher = "0", QString action = "");
    ScriptRule(const ScriptRule& value);
    ScriptRule& operator=(const ScriptRule& value);
    
    void setUp(const QDomNode& node);
    void print() const;
    
    void evaluate(QScriptEngine* matchEngine, QScriptEngine* actionEngine);
    void execute(QScriptEngine* actionEngine);

public:
    QString matcher;
    QString action;
};


class ScriptProcessor
{
public:
    ScriptProcessor(ScriptContext* context);
    
    void loadScript(const QString& path);
    void printRules() const;
    void printBindings() const;
    void print() const;
    
    void compileRules();
    void hardCompileRules();
    
    void preProcessBindings(QScriptEngine* matchEngine, QScriptEngine* actionEngine);
    void postProcessBindings(QScriptEngine* matchEngine, QScriptEngine* actionEngine);
    
//    void processCompiledRules();
    void processHardCompiledRules();
    void process();

private:
    ScriptContext* context;
    ScriptDataIterator iterator;
    QVector<ScriptDataBinding> bindings;
    QVector<ScriptRule> rules;
    
    QString compiledRule;
};

#endif // SCRIPTPROCESSOR_H
